package com.ng.common.fileupload.storage;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.Date;
import java.util.UUID;

import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import com.ng.common.exception.NgException;
import com.ng.common.fileupload.GsFileStorage;
import com.ng.common.fileupload.entity.UploadFileInfoEntity;
import com.ng.common.fileupload.entity.UploadFileInfoEntity.StoreType;
import com.ng.common.utils.DateUtils;
import com.ng.common.utils.StringUtils;

import cn.hutool.core.io.FileTypeUtil;
import io.minio.MinioClient;
import io.minio.policy.PolicyType;

/**
 * minio适配
 * @author lyf
 *
 */
@Component
@ConditionalOnProperty(prefix="ng.upload",name = "type", havingValue = "minio")
public class MinIOStorage implements GsFileStorage , Serializable {
	Logger logger = LoggerFactory.getLogger(getClass());
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 6279630274575904185L;

	
	@Value("${ng.upload.minio.endpoint:}")
	private String endPoint ;
	
 
	
	@Value("${ng.upload.minio.accessKey:}")
	private String accessKey ;
	
	@Value("${ng.upload.minio.secretKey:}")
	private String secretKey ;
	
	/**
	 * 存储桶名称
	 */
	@Value("${ng.upload.minio.bucketName:}")
	private String bucket;
	
	@Override
	public String getType() {
		// TODO Auto-generated method stub
		return "minio";
	}

	private MinioClient  client ;
	
	// 存储桶是否存在的标记缓存
	private boolean existsBucket = false;
 
	public MinioClient getClient() {
		if(client == null) {
			try {
				client = new MinioClient(endPoint, accessKey, secretKey);
				 
				// 检查存储桶是否已经存在
				if(!existsBucket) {
					existsBucket = client.bucketExists(bucket);
					if(!existsBucket) { 
						// 创建一个名为bucket的存储桶，用于存储文件。
						client.makeBucket(bucket);
						client.setBucketPolicy(bucket, "*", PolicyType.READ_ONLY);
					}
				}
				 
			} catch (Exception e) {
				// TODO Auto-generated catch block
				//e.printStackTrace();
				logger.error("minio存储未配置,文件上传异常!" , e);
				throw new NgException("minio存储未配置,文件上传异常!");
			}  

		}
		return client;
	}
	
	@Override
	public UploadFileInfoEntity uploadFile(MultipartFile file) {
		
		String name = file.getOriginalFilename();
  
		InputStream input = null;
		
		try {
			input = file.getInputStream();
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			//e.printStackTrace();
			logger.error("获取上传文件流异常!" , e);
			throw new NgException("获取上传文件流异常,文件上传失败!");
		}
		return upload(name, input, file.getSize(), file.getContentType());
	}
	
	private UploadFileInfoEntity upload(String name , InputStream input , long size , String contentType) {
	 
		MinioClient minioClient = getClient();
		String suffix = StringUtils.getSuffixName(name);
		
		 String month = DateUtils.format(new Date(), "yyyyMM");
		 
		
		// 生成一个新的名称
		String newName = month + "/" + UUID.randomUUID().toString().replace("-", "") + (org.apache.commons.lang.StringUtils.isNotBlank(suffix) ? "." + suffix : "");
		String url = null;
try {
			
			byte[] bs = IOUtils.toByteArray(input);
	        
	    	// 2021-09-17 lyf 增加文件格式验证 
			bs = validatorFile(bs, suffix);
			 
			input = new ByteArrayInputStream(bs);
			
			minioClient.putObject(bucket, newName, input , size, contentType);
			
			// 获取url
			url = minioClient.getObjectUrl(bucket, newName);
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			logger.error("minio文件上传异常!" , e);
			throw new NgException("文件上传异常!");
		} finally {
			if(input != null) {
				try {
					input.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}

		UploadFileInfoEntity atbAtta = new UploadFileInfoEntity();
		 

		atbAtta.setCreateTime(new Date());
	 
		
		atbAtta.setFileUrl(url);
		//

		atbAtta.setOldFileName(name);
		atbAtta.setFileSuffix(suffix);
		atbAtta.setFileSize(size);
		atbAtta.setNewFileName(newName);
		atbAtta.setStoreType(StoreType.minio.ordinal());
			
			 
		return atbAtta;
	}

	@Override
	public UploadFileInfoEntity uploadFile(File file) {
		String name = file.getName();
		  
		InputStream input = null;
		
		try {
			input = new FileInputStream(file);
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			//e.printStackTrace();
			logger.error("获取上传文件流异常!" , e);
			throw new NgException("获取上传文件流异常,文件上传失败!");
		}
		
		String contentType = FileTypeUtil.getType(file) ;// new MimetypesFileTypeMap().getContentType(file);
		
		return upload(name, input, file.length(), contentType);
	}

	@Override
	public void downFile(UploadFileInfoEntity entity, OutputStream out) {
		 
		MinioClient minioClient = getClient();
		
		try {
			InputStream input = minioClient.getObject(bucket, entity.getNewFileName());
			
			if(input == null) {
				throw new NgException("文件不存在或已被删除");
			}
			
			IOUtils.copy(input, out);
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			logger.error("获取文件异常, bucker:[{}] , name:[{}]" , bucket , entity.getNewFileName() , e);
		}
		
	}

	@Override
	public boolean deleteFile(UploadFileInfoEntity entity) {
		
		MinioClient minioClient = getClient();
		try {
			minioClient.removeObject(bucket, entity.getNewFileName());
			return true ;
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}  
		
		return false;
	}

}
